import json
import os
from datetime import datetime, date, timedelta
from config import app_state
from db import fetch_mysql_data
import csv


def update_persons():
    """带重试机制的数据更新"""
    with app_state.lock:
        last_choice = getattr(app_state, 'last_choice', None)
        if last_choice:
            choice = last_choice
            print(f"使用上一次选择的数据源: {choice}")
        else:
            print("请选择数据源: 1. 数据库 2. CSV文件")
            choice = input("输入数字选择: ")
        app_state.last_choice = choice
        if choice == "1":
            new_data = fetch_mysql_data()
            if new_data is not None:
                app_state.persons = new_data
                with open('person_data.tmp', 'w', encoding='utf-8') as f:
                    json.dump(new_data, f, ensure_ascii=False, default=lambda x: x.strftime('%Y-%m-%d') if isinstance(x, datetime) else x)
                print("内存数据更新成功，原子文件已同步")
            else:
                print("数据库数据加载失败")
                app_state.persons = []
        elif choice == "2":
            try:
                if os.path.exists('csv_data/csv_temp.csv'):
                    with open('csv_data/csv_temp.csv', 'r', encoding='gbk') as csvfile:
                        reader = csv.DictReader(csvfile)
                        data = [row for row in reader]
                    app_state.persons = data
                    with open('person_data.tmp', 'w', encoding='utf-8') as f:
                        json.dump(data, f, ensure_ascii=False, default=lambda x: x.strftime('%Y-%m-%d') if isinstance(x, datetime) else x)
                    print("使用CSV文件成功回退到本地缓存，原子文件已同步")
                else:
                    print("CSV文件不存在")
                    app_state.persons = []
            except Exception as e:
                print(f"CSV文件加载失败: {str(e)}")
                app_state.persons = []
        else:
            print("无效选择")
            app_state.persons = []


def get_shift_persons_simple(sorted_persons, week_offset, day_offset, shift_idx, shifts_per_day, persons_per_shift):
    """重新设计的排班算法：考虑跨周延续性、班次多样性、动态人数变化"""
    total_persons = len(sorted_persons)

    if total_persons == 0:
        return []

    # 如果房间人数少于每个班次需要的人数，直接返回所有人
    if total_persons <= persons_per_shift:
        return sorted_persons[:persons_per_shift]

    # 计算全局班次位置（跨周连续）
    global_shift_position = week_offset * 7 * shifts_per_day + day_offset * shifts_per_day + shift_idx

    # 使用轮转策略，但确保不重复选择同一个人
    persons = []
    used_indices = set()  # 记录已使用的人员索引

    for i in range(persons_per_shift):
        attempts = 0
        max_attempts = total_persons * 2  # 防止无限循环

        while attempts < max_attempts:
            # 基础轮转 + 班次偏移 + 随机偏移
            base_idx = (global_shift_position + i) % total_persons
            # 添加小的随机偏移，避免完全重复
            offset = (week_offset + day_offset + shift_idx + i) % 3
            person_idx = (base_idx + offset + attempts) % total_persons

            # 如果这个人员还没有被选择，则选择他
            if person_idx not in used_indices:
                persons.append(sorted_persons[person_idx])
                used_indices.add(person_idx)
                break

            attempts += 1

        # 如果尝试了多次仍然无法找到未使用的人员，选择第一个未使用的
        if attempts >= max_attempts:
            for j in range(total_persons):
                if j not in used_indices:
                    persons.append(sorted_persons[j])
                    used_indices.add(j)
                    break

    # 确保返回的join_date是字符串格式
    return [{
        **person,
        'join_date': person['join_date'].strftime('%Y-%m-%d') if isinstance(person['join_date'], date) else person['join_date']
    } for person in persons]


def get_shift_persons_advanced(sorted_persons, week_offset, day_offset, shift_idx, shifts_per_day, persons_per_shift):
    """高级排班算法：考虑跨周延续性、班次多样性、动态人数变化"""
    total_persons = len(sorted_persons)

    if total_persons == 0:
        return []

    # 如果房间人数少于每个班次需要的人数，直接返回所有人
    if total_persons <= persons_per_shift:
        return sorted_persons[:persons_per_shift]

    # 计算全局班次位置（跨周连续）
    global_shift_position = week_offset * 7 * shifts_per_day + day_offset * shifts_per_day + shift_idx

    # 使用轮转策略，但确保不重复选择同一个人
    persons = []
    used_indices = set()  # 记录已使用的人员索引

    for i in range(persons_per_shift):
        attempts = 0
        max_attempts = total_persons * 2  # 防止无限循环

        while attempts < max_attempts:
            # 基础轮转 + 班次偏移 + 随机偏移
            base_idx = (global_shift_position + i) % total_persons
            # 添加小的随机偏移，避免完全重复
            offset = (week_offset + day_offset + shift_idx + i) % 3
            person_idx = (base_idx + offset + attempts) % total_persons

            # 如果这个人员还没有被选择，则选择他
            if person_idx not in used_indices:
                persons.append(sorted_persons[person_idx])
                used_indices.add(person_idx)
                break

            attempts += 1

        # 如果尝试了多次仍然无法找到未使用的人员，选择第一个未使用的
        if attempts >= max_attempts:
            for j in range(total_persons):
                if j not in used_indices:
                    persons.append(sorted_persons[j])
                    used_indices.add(j)
                    break

    # 确保返回的join_date是字符串格式
    return [{
        **person,
        'join_date': person['join_date'].strftime('%Y-%m-%d') if isinstance(person['join_date'], date) else person['join_date']
    } for person in persons]


def get_shift_persons_smart(sorted_persons, week_offset, day_offset, shift_idx, shifts_per_day, persons_per_shift,
                            person_shift_history=None, person_weekly_counts=None):
    """智能排班算法：考虑跨周延续性、班次多样性、动态人数变化"""
    total_persons = len(sorted_persons)

    if total_persons == 0:
        return []

    # 修复错误代码并清理之前错误插入的代码

    # 如果房间人数少于每个班次需要的人数，直接返回所有人
    if total_persons <= persons_per_shift:
        return sorted_persons[:persons_per_shift]

    # 初始化历史记录（如果未提供）
    if person_shift_history is None:
        person_shift_history = {person['name']: {'shifts': [], 'last_shift': None, 'last_day': None} for person in
                                sorted_persons}

    if person_weekly_counts is None:
        person_weekly_counts = {person['name']: 0 for person in sorted_persons}

    # 计算全局班次位置（跨周连续）
    global_shift_position = week_offset * 7 * shifts_per_day + day_offset * shifts_per_day + shift_idx

    # 班次名称映射
    shift_names = ['午班', '夜班1', '夜班2', '夜班3']
    current_shift = shift_names[shift_idx]

    # 预过滤：排除违反约束的人员
    available_persons = []
    for person_idx, person in enumerate(sorted_persons):
        # 检查是否今天已经值过班
        today_shifts = [s for s in person_shift_history[person['name']]['shifts']
                        if s['week'] == week_offset and s['day'] == day_offset]
        if today_shifts:
            continue  # 今天已经值过班，跳过

        # 检查是否昨天值过班（连续值班）
        if day_offset > 0:  # 不是第一天
            yesterday_shifts = [s for s in person_shift_history[person['name']]['shifts']
                                if s['week'] == week_offset and s['day'] == day_offset - 1]
            if yesterday_shifts:
                continue  # 昨天值过班，跳过

        # 检查是否跨周连续值班
        if week_offset > 0 and day_offset == 0:  # 新的一周第一天
            last_week_shifts = [s for s in person_shift_history[person['name']]['shifts']
                                if s['week'] == week_offset - 1 and s['day'] == 6]
            if last_week_shifts:
                continue  # 上周日值过班，跳过

        available_persons.append((person_idx, person))

    # 如果没有可用人员，放宽约束（只检查同一天多班）
    if len(available_persons) < persons_per_shift:
        available_persons = []
        for person_idx, person in enumerate(sorted_persons):
            # 只检查是否今天已经值过班
            today_shifts = [s for s in person_shift_history[person['name']]['shifts']
                            if s['week'] == week_offset and s['day'] == day_offset]
            if not today_shifts:  # 今天没有值过班
                available_persons.append((person_idx, person))

    # 如果仍然没有足够的人员，返回空列表
    if len(available_persons) < persons_per_shift:
        return []

    # 选择人员，考虑多个因素
    persons = []
    used_indices = set()

    for i in range(persons_per_shift):
        best_person = None
        best_score = float('-inf')

        # 为每个位置找到最佳人选
        for person_idx, person in available_persons:
            if person_idx in used_indices:
                continue

            # 计算该人员的综合评分
            score = calculate_person_score(
                person, person_idx, global_shift_position, total_persons,
                current_shift, person_shift_history, person_weekly_counts,
                week_offset, day_offset, shift_idx
            )

            if score > best_score:
                best_score = score
                best_person = (person_idx, person)

        if best_person:
            person_idx, person = best_person
            persons.append(person)
            used_indices.add(person_idx)

            # 更新历史记录 - 存储详细的班次信息
            shift_info = {
                'shift': current_shift,
                'day': day_offset,
                'week': week_offset,
                'shift_idx': shift_idx
            }
            person_shift_history[person['name']]['shifts'].append(shift_info)
            person_shift_history[person['name']]['last_shift'] = current_shift
            person_shift_history[person['name']]['last_day'] = day_offset
            person_weekly_counts[person['name']] += 1

    # 确保返回的join_date是字符串格式
    return [{
        **person,
        'join_date': person['join_date'].strftime('%Y-%m-%d') if isinstance(person['join_date'], date) else person['join_date']
    } for person in persons]


def calculate_person_score(person, person_idx, global_shift_position, total_persons,
                           current_shift, person_shift_history, person_weekly_counts,
                           week_offset, day_offset, shift_idx):
    """计算人员的综合评分"""
    score = 0

    # 1. 基础轮转分数
    score += _calculate_rotation_score(global_shift_position, person_idx, total_persons)

    # 2. 班次多样性分数
    score += _calculate_shift_diversity_score(person_shift_history[person['name']]['shifts'], current_shift)

    # 3. 本周值班次数平衡分数
    score += _calculate_weekly_balance_score(person_weekly_counts[person['name']])

    # 4. 班次权重分数
    score += _calculate_shift_weight_score(current_shift)

    # 5. 困难班次平衡分数（鼓励承担困难班次较少的人员）
    difficult_shifts_count = sum(1 for s in person_shift_history[person['name']]['shifts'] 
                                if s['shift'] in ['夜班2', '夜班3'])
    # 计算平均困难班次数
    total_difficult_shifts = sum(
        sum(1 for s in person_shift_history[name]['shifts'] if s['shift'] in ['夜班2', '夜班3'])
        for name in person_shift_history
    )
    avg_difficult_shifts = total_difficult_shifts / len(person_shift_history) if person_shift_history else 0
    score += _calculate_difficult_shifts_balance_score(difficult_shifts_count, avg_difficult_shifts)

    # 6. 随机因子

    return score


def _calculate_rotation_score(global_shift_position, person_idx, total_persons):
    """计算基础轮转分数"""
    return ((global_shift_position + person_idx) % total_persons) * 10


def _calculate_shift_diversity_score(shift_history, current_shift):
    """计算班次多样性分数"""
    score = 0
    if shift_history:
        recent_shifts = [s['shift'] for s in shift_history[-3:]]
        if len(recent_shifts) >= 2 and len(set(recent_shifts)) == 1:
            score -= 50
        if shift_history[-1]['shift'] == current_shift:
            score -= 30
    return score


def _calculate_weekly_balance_score(weekly_count):
    """计算本周值班次数平衡分数"""
    if weekly_count >= 4:
        return -100
    elif weekly_count <= 1:
        return 50
    return 0


def _calculate_shift_weight_score(current_shift):
    """优化班次权重分配，使权重差异更加均衡"""
    shift_weights = {
        '午班': 1.0,
        '夜班1': 1.2,
        '夜班2': 1.4,
        '夜班3': 1.6
    }
    return shift_weights.get(current_shift, 1.0)


def _calculate_difficult_shifts_balance_score(difficult_shifts_count, avg_difficult_shifts):
    """计算困难班次平衡分数，鼓励承担困难班次较少的人员"""
    # 根据承担的困难班次数量与平均值的差异给予加分，承担越少加分越多
    # 使用更动态的调整机制
    diff_from_avg = difficult_shifts_count - avg_difficult_shifts
    
    if diff_from_avg <= -2:
        return 40  # 远低于平均值，给予较高加分
    elif diff_from_avg <= -1:
        return 25  # 低于平均值，给予中等加分
    elif diff_from_avg <= 0:
        return 10  # 接近平均值，给予少量加分
    elif diff_from_avg <= 1:
        return -10  # 略高于平均值，轻微减分
    elif diff_from_avg <= 2:
        return -25  # 高于平均值，中等减分
    else:
        return -40  # 远高于平均值，较多减分


def generate_schedule(room_persons, base_date=None):
    """生成房间的排班表 - 使用智能算法确保公平性和多样性"""
    if not room_persons:
        return []

    # 按入所日期排序
    sorted_persons = sorted(room_persons, key=lambda x: x['join_date'])
    total_persons = len(sorted_persons)

    # 使用传入日期或当前日期
    today = base_date if base_date else datetime.now().date()

    # 计算轮次偏移量 - 基于累计周数
    start_date = date(2023, 1, 1)  # 系统起始日期
    weeks_since_start = (today - start_date).days // 7

    # 生成新的排班表
    new_schedule = []
    shifts = ['午班', '夜班1', '夜班2', '夜班3']
    shifts_per_day = len(shifts)
    persons_per_shift = 2

    # 计算本周一的日期
    days_to_monday = today.weekday()  # 0=周一, 6=周日
    monday = today - timedelta(days=days_to_monday)

    # 初始化人员历史记录和本周计数
    person_shift_history = {person['name']: {'shifts': [], 'last_shift': None} for person in sorted_persons}
    person_weekly_counts = {person['name']: 0 for person in sorted_persons}

    # 检查人员变动（新进或离开）
    current_names = {person['name'] for person in sorted_persons}
    for name in list(person_shift_history.keys()):
        if name not in current_names:
            del person_shift_history[name]
            del person_weekly_counts[name]

    # 生成完整一周的排班数据
    new_schedule = []
    for day_offset in range(7):
        day_date = monday + timedelta(days=day_offset)
        date_str = day_date.strftime('%Y-%m-%d')
        
        # 生成新的排班数据
        day_schedule = {}
        for shift_idx, shift in enumerate(shifts):
            persons = get_shift_persons_smart(
                sorted_persons,
                weeks_since_start,
                day_offset,
                shift_idx,
                shifts_per_day,
                persons_per_shift,
                person_shift_history,
                person_weekly_counts
            )
            day_schedule[shift] = persons
        
        new_schedule.append({'date': day_date.strftime('%Y-%m-%d'), 'shifts': day_schedule})
    
    # 先应用公平性优化
    optimized_schedule = balance_schedule_fairness(new_schedule, sorted_persons)
    
    # 调取当周历史记录
    from db import get_schedule_by_date
    result = get_schedule_by_date(room_persons[0]['room_number'], today.strftime('%Y-%m-%d'))
    try: 
        schedule_data = result['schedule_data']
        history_schedule = json.loads(schedule_data)
    except(ValueError, TypeError, KeyError) as e:
        history_schedule = None

    # 合并历史数据和优化数据
    final_schedule = []

    # 1. 强制添加所有有效历史数据(今日之前)
    if isinstance(history_schedule, list):
        for day_data in history_schedule:
            # 严格验证数据格式
            if not isinstance(day_data, dict):
                continue
                
            date_str = day_data.get('date')
            shifts_data = day_data.get('shifts')
            
            # 必须包含date和shifts字段
            if not date_str or not shifts_data or not isinstance(shifts_data, dict):
                continue
                
            try:
                # 解析日期
                day_date = datetime.strptime(str(date_str), '%Y-%m-%d').date()
                
                # 只保留今日之前的数据
                if day_date < today:
                    # 深拷贝数据避免污染
                    final_schedule.append({
                        'date': day_date.strftime('%Y-%m-%d'),
                        'shifts': dict(shifts_data)
                    })
                    
            except (ValueError, TypeError, AttributeError) as e:
                print(f"[WARN] 无效的排班数据格式: {date_str}, 错误: {str(e)}")
    
        # 2. 然后添加优化数据(今日及以后)
        for opt_day in optimized_schedule:
            if isinstance(opt_day, dict) and 'date' in opt_day:
                try:
                    day_date = datetime.strptime(opt_day['date'], '%Y-%m-%d').date()
                    if day_date >= today:
                        # 查找并替换相同日期的数据
                        replaced = False
                        for i, existing in enumerate(final_schedule):
                            if isinstance(existing, dict) and existing.get('date') == opt_day['date']:
                                final_schedule[i] = opt_day
                                replaced = True
                                break
                        # 只有在没有找到匹配日期时才添加新数据
                        if not replaced:
                            # 检查是否已经存在相同日期的数据
                            date_exists = any(isinstance(item, dict) and item.get('date') == opt_day['date'] for item in final_schedule)
                            if not date_exists:
                                final_schedule.append(opt_day)
                except (ValueError, TypeError):
                    continue
    
    # 3. 如果没有历史数据，则使用全部优化数据
    if not final_schedule and optimized_schedule:
        final_schedule = optimized_schedule

    return final_schedule


def balance_schedule_fairness(schedule, sorted_persons):
    """改进公平性平衡算法，满足以下要求：
    1. 实现排班的延续性
    2. 各人的总值班次数差距不应超过1次
    3. 不应连续值班（同一天内）
    4. 在此基础上考虑值班权重离散值的平均化
    """
    total_persons = len(sorted_persons)
    if total_persons <= 2:
        return schedule  # 人数太少，不需要平衡

    # 初始化统计数据
    person_counts = {person['name']: {'total': 0, 'difficult': 0} for person in sorted_persons}

    for day in schedule:
        for shift_name, shift_persons in day['shifts'].items():
            for person in shift_persons:
                if person and person.get('name'):
                    person_counts[person['name']]['total'] += 1
                    if shift_name in ['夜班2', '夜班3']:
                        person_counts[person['name']]['difficult'] += 1

    # 平衡总值班次数差距
    max_count = max(person_counts[p]['total'] for p in person_counts)
    min_count = min(person_counts[p]['total'] for p in person_counts)
    if max_count - min_count > 1:
        # 尝试交换班次以平衡总值班次数
        for day in schedule:
            for shift_name, shift_persons in day['shifts'].items():
                for i, person in enumerate(shift_persons):
                    if person and person.get('name') and person_counts[person['name']]['total'] == max_count:
                        # 找到值班次数最少的人进行交换
                        for p in sorted_persons:
                            if person_counts[p['name']]['total'] == min_count and not violates_consecutive_shifts(schedule, day, shift_name, p['name']):
                                shift_persons[i] = p
                                person_counts[person['name']]['total'] -= 1
                                person_counts[p['name']]['total'] += 1
                                break

    # 平衡困难班次分配
    for day in schedule:
        for shift_name, shift_persons in day['shifts'].items():
            if shift_name in ['夜班2', '夜班3']:
                shift_persons.sort(key=lambda p: person_counts[p['name']]['difficult'] if p else float('inf'))

    return schedule


def try_swap_shift(schedule, from_person, to_person, person_counts):
    """尝试将一个人的班次替换给另一个人"""
    # 首先找到目标人员对象
    to_person_obj = None
    for day in schedule:
        for shift_name, shift_persons in day['shifts'].items():
            for person in shift_persons:
                if person and person.get('name') == to_person:
                    to_person_obj = person
                    break
            if to_person_obj:
                break
        if to_person_obj:
            break

    if not to_person_obj:
        return False

    # 遍历所有班次，找到可以替换的机会
    for day in schedule:
        for shift_name, shift_persons in day['shifts'].items():
            # 检查这个班次是否包含要替换的人
            if any(p.get('name') == from_person for p in shift_persons):
                # 检查目标人员是否已经在其他班次中
                if not any(p.get('name') == to_person for p in shift_persons):
                    # 检查是否违反连续值班约束
                    if not violates_consecutive_shifts(schedule, day, shift_name, to_person):
                        # 可以替换
                        for i, person in enumerate(shift_persons):
                            if person.get('name') == from_person:
                                shift_persons[i] = to_person_obj
                                return True
    return False

def violates_consecutive_shifts(schedule, target_day, target_shift, person_name):
    """检查替换是否会导致连续值班"""
    # 这里可以实现更复杂的连续值班检查逻辑
    # 简化版本：只检查同一天内是否有其他班次
    for shift_name, shift_persons in target_day['shifts'].items():
        if shift_name != target_shift:
            if any(p.get('name') == person_name for p in shift_persons):
                return True
    return False

def try_complex_swap(schedule, over_staffed, under_staffed, person_counts):
    """尝试更复杂的班次交换"""
    # 实现更复杂的交换逻辑
    # 例如，可以尝试多个人员之间的循环交换
    
    # 简化版本：尝试从值班次数最多的人员向最少的人员进行交换
    if not over_staffed or not under_staffed:
        return False
    
    from_person = over_staffed[0]
    to_person = under_staffed[0]
    
    return try_swap_shift(schedule, from_person, to_person, person_counts)


def generate_bed_layout(room_persons):
    """生成铺位布局"""
    if not room_persons:
        return []

    # 按入所日期排序
    sorted_persons = sorted(room_persons, key=lambda x: x['join_date'])

    # 生成铺位布局数据
    bed_layout = []
    for i, person in enumerate(sorted_persons):
        bed_layout.append({
            'id': person['id'],
            'name': person['name'],
            'join_date': person['join_date'],
            'FXDJ': person['FXDJ'],
            'BHLX': person['BHLX'],
            'position': f"铺位{i + 1}"
        })

    return bed_layout


def generate_seat_layout(room_persons):
    """生成座位布局"""
    if not room_persons:
        return []

    # 按入所日期排序
    sorted_persons = sorted(room_persons, key=lambda x: x['join_date'])

    # 生成座位布局数据
    seat_layout = []
    for i, person in enumerate(sorted_persons):
        seat_layout.append({
            'id': person['id'],
            'name': person['name'],
            'join_date': person['join_date'],
            'position': f"座位{i + 1}"
        })

    return seat_layout


def validate_schedule_fairness(schedule, room_persons):
    """验证排班表的公平性，返回详细的统计信息"""
    if not schedule or not room_persons:
        return None

    # 定义班次权重
    shift_weights = {
        '午班': 1.0,
        '夜班1': 1.2,
        '夜班2': 1.5,
        '夜班3': 1.5
    }

    # 初始化统计
    stats = {
        'total_persons': len(room_persons),
        'total_shifts': 0,
        'person_stats': {},
        'shift_type_stats': {},
        'fairness_score': 0,
        'issues': [],
        'shift_weight_stats': {
            '午班': 0,
            '夜班1': 0,
            '夜班2': 0,
            '夜班3': 0
        }
    }

    # 初始化人员统计
    for person in room_persons:
        stats['person_stats'][person['name']] = {
            'total': 0,
            '午班': 0,
            '夜班1': 0,
            '夜班2': 0,
            '夜班3': 0,
            'difficult_shifts': 0  # 困难班次统计
        }

    # 初始化班次类型统计
    stats['shift_type_stats'] = {
        '午班': 0,
        '夜班1': 0,
        '夜班2': 0,
        '夜班3': 0
    }

    # 统计排班数据
    for day in schedule:
        for shift_name, shift_persons in day['shifts'].items():
            if shift_persons:
                stats['total_shifts'] += len(shift_persons)
                stats['shift_type_stats'][shift_name] += len(shift_persons)

                # 统计班次权重
                stats['shift_weight_stats'][shift_name] += len(shift_persons) * shift_weights.get(shift_name, 1.0)

                for person in shift_persons:
                    if isinstance(person, dict) and 'name' in person:
                        # 初始化人员统计项（如果不存在）
                        if person['name'] not in stats['person_stats']:
                            stats['person_stats'][person['name']] = {
                                'total': 0,
                                'difficult_shifts': 0,
                                '午班': 0,
                                '夜班1': 0,
                                '夜班2': 0,
                                '夜班3': 0
                            }
                        stats['person_stats'][person['name']]['total'] += 1
                        if shift_name in stats['person_stats'][person['name']]:
                            stats['person_stats'][person['name']][shift_name] += 1
                        # 统计加权班次
                        stats['person_stats'][person['name']]['difficult_shifts'] += shift_weights.get(shift_name, 1.0)

    # 计算公平性分数
    total_persons = len(room_persons)
    expected_shifts_per_person = stats['total_shifts'] / total_persons if total_persons > 0 else 0

    # 计算方差（值越小越公平）
    variance = 0
    for person_name, person_stats in stats['person_stats'].items():
        diff = person_stats['total'] - expected_shifts_per_person
        variance += diff * diff

    variance = variance / total_persons if total_persons > 0 else 0
    stats['fairness_score'] = max(0, 100 - variance * 10)  # 转换为0-100的分数

    # 检查问题
    if variance > 2:
        stats['issues'].append(f"值班次数分布不均，方差: {variance:.2f}")

    # 检查是否有人的值班次数过多或过少
    for person_name, person_stats in stats['person_stats'].items():
        if person_stats['total'] > expected_shifts_per_person + 2:
            stats['issues'].append(f"{person_name} 值班次数过多: {person_stats['total']}")
        elif person_stats['total'] < expected_shifts_per_person - 2:
            stats['issues'].append(f"{person_name} 值班次数过少: {person_stats['total']}")

    # 计算每个人的加权值班分数
    weighted_scores = [person_stats['difficult_shifts'] for person_stats in stats['person_stats'].values()]
    avg_weighted_score = sum(weighted_scores) / len(weighted_scores) if weighted_scores else 0
    
    # 计算加权分数的方差
    weighted_variance = sum((score - avg_weighted_score) ** 2 for score in weighted_scores) / len(weighted_scores) if weighted_scores else 0
    
    # 检查加权分数的离散程度
    if weighted_variance > 0.5:  # 阈值可以根据需要调整
        stats['issues'].append(f"加权值班分数分布不均，方差: {weighted_variance:.2f}")
    
    # 检查是否有人的加权值班分数过高或过低
    for person_name, person_stats in stats['person_stats'].items():
        if person_stats['difficult_shifts'] > avg_weighted_score + 1.0:  # 阈值可以根据需要调整
            stats['issues'].append(f"{person_name} 加权值班分数过高: {person_stats['difficult_shifts']:.2f}")
        elif person_stats['difficult_shifts'] < avg_weighted_score - 1.0:  # 阈值可以根据需要调整
            stats['issues'].append(f"{person_name} 加权值班分数过低: {person_stats['difficult_shifts']:.2f}")

    return stats


def get_schedule_analysis(schedule, room_persons):
    """优化分析报告功能，提供更详细的公平性分析和建议"""
    if not schedule or not room_persons:
        return None

    # 定义班次权重
    shift_weights = {
        '午班': 1.0,
        '夜班1': 1.2,
        '夜班2': 1.5,
        '夜班3': 1.5
    }

    # 初始化统计
    stats = {
        'total_persons': len(room_persons),
        'total_shifts': 0,
        'person_stats': {},
        'shift_type_stats': {},
        'fairness_score': 0,
        'issues': [],
        'shift_weight_stats': {
            '午班': 0,
            '夜班1': 0,
            '夜班2': 0,
            '夜班3': 0
        }
    }

    # 初始化人员统计
    for person in room_persons:
        stats['person_stats'][person['name']] = {
            'total': 0,
            '午班': 0,
            '夜班1': 0,
            '夜班2': 0,
            '夜班3': 0,
            'difficult_shifts': 0  # 困难班次统计
        }

    # 初始化班次类型统计
    stats['shift_type_stats'] = {
        '午班': 0,
        '夜班1': 0,
        '夜班2': 0,
        '夜班3': 0
    }

    # 统计班次数据
    for day in schedule:
        for shift_name, shift_persons in day['shifts'].items():
            stats['shift_type_stats'][shift_name] += len(shift_persons)
            stats['shift_weight_stats'][shift_name] += shift_weights[shift_name] * len(shift_persons)
            stats['total_shifts'] += len(shift_persons)
            for person in shift_persons:
                if person and person.get('name'):
                    stats['person_stats'][person['name']]['total'] += 1
                    stats['person_stats'][person['name']][shift_name] += 1
                    if shift_name in ['夜班2', '夜班3']:
                        stats['person_stats'][person['name']]['difficult_shifts'] += 1

    # 计算公平性评分
    fairness_scores = []
    for person_name, person_data in stats['person_stats'].items():
        fairness_scores.append(person_data['total'])
    stats['fairness_score'] = round(sum(fairness_scores) / len(fairness_scores), 2)

    # 检查问题
    for person_name, person_data in stats['person_stats'].items():
        if person_data['total'] == 0:
            stats['issues'].append(f"人员 {person_name} 未被分配任何班次")
        if person_data['total'] > stats['fairness_score'] + 2:
            stats['issues'].append(f"人员 {person_name} 的值班次数过多")
        if person_data['total'] < stats['fairness_score'] - 2:
            stats['issues'].append(f"人员 {person_name} 的值班次数过少")

    # 生成分析报告
    report = f"总人数: {stats['total_persons']}\n"
    report += f"参与值班人数: {len([p for p in stats['person_stats'] if stats['person_stats'][p]['total'] > 0])}\n"
    report += f"总班次数: {stats['total_shifts']}\n"
    report += f"公平性评分: {stats['fairness_score']}\n"
    report += f"加权值班分数基准值: {round(sum(stats['shift_weight_stats'].values()) / stats['total_persons'], 2)}\n"

    if stats['issues']:
        report += "\n问题列表:\n" + "\n".join(stats['issues'])
    else:
        report += "\n无明显问题\n"

    return report


def get_schedule_analysis(schedule, room_persons, total_persons_count=None):
    """获取并打印排班表的详细分析报告"""
    validation = validate_schedule_fairness(schedule, room_persons)
    if not validation:
        print("无法分析排班表")
        return

    # 计算参与值班的人数（排除FXDJ不为'0'和BHLX不为'0'的人员）
    participating_persons = [
        p for p in room_persons 
        if str(p.get('FXDJ', '0')) == '0' and str(p.get('BHLX', '0')) == '0'
    ]
    participating_count = len(participating_persons)
    
    # 如果没有传入总人数，则使用room_persons的长度
    if total_persons_count is None:
        total_persons_count = len(room_persons)

    # 计算加权值班分数的基准值
    weighted_scores = [person_stats['difficult_shifts'] for person_stats in validation['person_stats'].values()]
    avg_weighted_score = sum(weighted_scores) / len(weighted_scores) if weighted_scores else 0

    # 构建报告内容
    report = f"""
房间总人数: {total_persons_count}
参与值班人数: {participating_count}
总班次数: {validation['total_shifts']}
公平性评分: {validation['fairness_score']:.1f}/100
加权值班分数基准值: {avg_weighted_score:.2f}
*加权值班分数计算公式: 每个班次的权重累加 (午班: 1.0, 夜班1: 1.2, 夜班2: 1.5, 夜班3: 1.5)
"""

    if validation['issues']:
        report += "\n" + "=" * 50 + "\n发现的问题:\n"
        for issue in validation['issues']:
            report += f"- {issue}\n"
    else:
        report += "\n" + "=" * 50 + "\n无明显问题\n"

    return report
